package py.una.pol.ii.is2.facade.taller;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import py.una.pol.ii.is2.dao.flota.PlantillaMantenimientoDAO;
import py.una.pol.ii.is2.dao.flota.VehiculoDAO;
import py.una.pol.ii.is2.dao.taller.DetalleOrdenMantenimientoPlantillaDAOLocal;
import py.una.pol.ii.is2.dao.taller.OrdenMantenimientoDAOLocal;
import py.una.pol.ii.is2.domain.flota.DetallePlantillaMantenimiento;
import py.una.pol.ii.is2.domain.flota.PlantillaMantenimiento;
import py.una.pol.ii.is2.domain.flota.Vehiculo;
import py.una.pol.ii.is2.domain.sistema.Usuario;
import py.una.pol.ii.is2.domain.taller.DetalleOrdenMantenimientoInsumo;
import py.una.pol.ii.is2.domain.taller.DetalleOrdenMantenimientoPlantilla;
import py.una.pol.ii.is2.domain.taller.OrdenMantenimiento;

/**
 * Clase que implementa la interfaz OrdenMantenimientoFacade
 * @author mbenitez
 * @date 14/05/2009
 */
@Stateless(mappedName = "ordenFacade")
public class OrdenMantenimientoFacadeBean implements OrdenMantenimientoFacadeLocal, OrdenMantenimientoFacadeRemote {

    /**
     * Manejador de instancias de entidades para recuperar instancias desde
     * el Persistence Context
     */
    @PersistenceContext
    private EntityManager em;
    @EJB
    private OrdenMantenimientoDAOLocal ordenMantenimientoDao;
    @EJB
    private DetalleOrdenMantenimientoPlantillaDAOLocal detalleMantenimientoPlantillaDao;
    private OrdenMantenimiento orden;
    private DetalleOrdenMantenimientoPlantilla detallePlantilla;
    @EJB
    private VehiculoDAO vehiculoDao;
    private Vehiculo vehiculo;
    @EJB
    private PlantillaMantenimientoDAO plantillaDao;

    /**
     * Aprueba la orden y da por ejecutado todos los mantenimientos.
     * @param codObjeto Código de la orden de mantenimiento
     * @return true para señalar que la operación se ejecutó con éxito, en otro caso false.
     * @throws java.lang.Exception
     */
    public boolean aprobarOrden(Integer codObjeto) throws Exception {
        boolean retorno = false;
        try {
            orden = ordenMantenimientoDao.find(codObjeto);
            orden.setOrmaEstado(OrdenMantenimiento.estadoEjecutada);
            for (DetalleOrdenMantenimientoPlantilla plantilla : orden.getDetalleOrdenMantenimientoPlantillaCollection()) {
                detallePlantilla = plantilla;
                aprobarMantenimiento();
            }
            ordenMantenimientoDao.update(orden);
            retorno = true;
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            return retorno;
        }
    }

    /**
     * Da por ejecutado un mantenimiento
     * @param codMantenimiento Código del mantenimiento
     * @return true para señalar que la operación se ejecutó con éxito, en otro caso false.
     * @throws java.lang.Exception
     */
    public boolean aprobarMantenimiento(Integer codMantenimiento) throws Exception {
        boolean retorno = false;
        try {
            detallePlantilla = detalleMantenimientoPlantillaDao.find(codMantenimiento);
            aprobarMantenimiento();
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            return retorno;
        }
    }

    /**
     * Lista los detalles plantilla de la orden de mantenimiento
     * @param codObjeto Código de la orden de mantenimiento
     * @return listaPlantilla Lista de resultados arrojados por la ejecución de la consulta, en otro caso null.
     * @throws java.lang.Exception
     */
    public List<DetalleOrdenMantenimientoPlantilla> getListaPlantilla(Integer codObjeto) throws Exception {
        List<DetalleOrdenMantenimientoPlantilla> listaPlantilla = null;
        try {
            orden = ordenMantenimientoDao.find(codObjeto);
            listaPlantilla = detalleMantenimientoPlantillaDao.getListaDetalleOrdenMantenimientoPlantillasByCodOrden(orden);
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            return listaPlantilla;
        }

    }

    /**
     * Lista los detalles insumo de la plantilla de mantenimiento
     * @param codObjeto Código de la plantilla de mantenimiento
     * @return listaInsumo Lista de resultados arrojados por la ejecución de la consulta, en otro caso null.
     * @throws java.lang.Exception
     */
    public List<DetalleOrdenMantenimientoInsumo> getListaInsumos(Integer codObjeto) throws Exception {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    /**
     * Inserta una entidad de OrdenMantenimiento en la base de datos.
     * @param fecha Fecha de ejecución de la operación
     * @param usuario Usuario que realiza la operación
     * @param nroChapaVehiculo Número de chapa del vehículo
     * @param listaCodPlantillas Lista de código de plantillas asignadas a la orden de mantenimiento
     * @param estado Estado de la orden de mantenimiento
     * @return true para señalar que la operación se ejecutó con éxito, en otro caso false.
     * @throws java.lang.Exception
     */
    public boolean insert(Date fecha, Usuario usuario, String nroChapaVehiculo, String[] listaCodPlantillas, Integer estado) throws Exception {
        boolean retorno = false;
        try {
            // creamos e insertamos la entidad
            orden = new OrdenMantenimiento();
            vehiculo = vehiculoDao.findByNroChapa(nroChapaVehiculo);
            List<DetalleOrdenMantenimientoPlantilla> listaDetallePlantillas = generarDetalles(listaCodPlantillas, "insert");
            orden.setDetalleOrdenMantenimientoPlantillaCollection(listaDetallePlantillas);
            orden.setOrmaCodUsuario(usuario);
            orden.setOrmaCodVehiculo(vehiculo);
            orden.setOrmaEstado(estado);
            orden.setOrmaFecha(fecha);
            orden.setOrmaCostoTotal(calculaCostoTotal(listaDetallePlantillas));


            //if (ordenMantenimientoDao.update(orden)) {
            if (ordenMantenimientoDao.insert(orden)) {
                retorno = true;
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            return retorno;
        }
    }

    /**
     * Modifica una entidad de OrdenMantenimiento en la base de datos.
     * @param orden OrdenMantenimiento a editar
     * @param fecha Fecha de ejecución de la operación
     * @param usuario Usuario que realiza la operación
     * @param nroChapaVehiculo Número de la chapa del vehículo
     * @param listaCodPlantillas Lista de código de plantillas asignadas a la orden de mantenimiento
     * @param estado Estado de la orden de mantenimiento
     * @return true para señalar que la operación se ejecutó con éxito, en otro caso false.
     * @throws java.lang.Exception
     */
    public boolean update(OrdenMantenimiento orden, Date fecha, Usuario usuario, String nroChapaVehiculo, String[] listaCodPlantillas, Integer estado) throws Exception {
        boolean retorno = false;
        try {
            // creamos e insertamos la entidad
            this.orden = orden;
            vehiculo = vehiculoDao.findByNroChapa(nroChapaVehiculo);
            List<DetalleOrdenMantenimientoPlantilla> listaDetallePlantillas = generarDetalles(listaCodPlantillas, "update");
            this.orden.setDetalleOrdenMantenimientoPlantillaCollection(listaDetallePlantillas);
            this.orden.setOrmaCodUsuario(usuario);
            this.orden.setOrmaCodVehiculo(vehiculo);
            this.orden.setOrmaEstado(estado);
            this.orden.setOrmaFecha(fecha);
            this.orden.setOrmaCostoTotal(calculaCostoTotal(listaDetallePlantillas));

            if (ordenMantenimientoDao.update(this.orden)) {
                retorno = true;
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            return retorno;
        }
    }

    /***
     *
     * Calcula el costo total de la orden generada.
     */
    public BigInteger calculaCostoTotal(List<DetalleOrdenMantenimientoPlantilla> listaDetallePlantillas) {
        BigInteger costoTotal = BigInteger.ZERO;
        for (DetalleOrdenMantenimientoPlantilla detalleOrdenMantenimientoPlantilla : listaDetallePlantillas) {
            costoTotal = costoTotal.add(detalleOrdenMantenimientoPlantilla.getDeopCostoInsumos().add(detalleOrdenMantenimientoPlantilla.getDeopCostoManoObra()));
        }
        return costoTotal;
    }

    /**
     * Metodo que cambia el estado de un detalle (mantenimiento) de una orden de mantenimiento
     * a EJECUTADO.
     *
     * @return
     */
    private boolean aprobarMantenimiento() {
        boolean retorno = false;
        try {
            detallePlantilla.setDeopEstado(DetalleOrdenMantenimientoPlantilla.estadoEjecutado);
            detalleMantenimientoPlantillaDao.update(detallePlantilla);
            chequearOrden();
            retorno = true;
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            return retorno;
        }
    }

    /**
     *
     * Metodo que verifica en cada aprobacion de un detalle (mantenimiento),
     * si la orden se debe poner a estado EJECUTADA.
     *
     */
    private void chequearOrden() throws Exception {
        orden = detallePlantilla.getDeopCodOrden();
        boolean setEjecutada = true;
        for (DetalleOrdenMantenimientoPlantilla detalle : orden.getDetalleOrdenMantenimientoPlantillaCollection()) {
            if (detalle.getDeopEstado() == 0) {
                setEjecutada = false;
            }
        }

        // si no se encuentra ningun detalle pendiente se cambia el estado de la orden a EJECUTADA
        if (setEjecutada) {
            orden.setOrmaEstado(OrdenMantenimiento.estadoEjecutada);
            ordenMantenimientoDao.update(orden);
        } else if (orden.getOrmaEstado() == OrdenMantenimiento.estadoPendiente) {
            // si la orden se encuentra en estado PENDIENTE se cambia el estado de la orden a ENEJECUCION
            orden.setOrmaEstado(OrdenMantenimiento.estadoEnEjecucion);
            ordenMantenimientoDao.update(orden);
        }
    }

    public List<DetalleOrdenMantenimientoPlantilla> generarDetalles(String[] listaPlantillasSelected, String operacion) throws Exception {
        // se generan los detalles
        PlantillaMantenimiento plantilla;
        DetalleOrdenMantenimientoPlantilla detalleOrdenPlantilla;
        List<DetalleOrdenMantenimientoPlantilla> listaDetallePlantillas = new ArrayList<DetalleOrdenMantenimientoPlantilla>();

        // se iteran sobre las plantillas seleccionadas
        for (String codPlantilla : listaPlantillasSelected) {
            plantilla = plantillaDao.find(Integer.parseInt(codPlantilla));
            detalleOrdenPlantilla = isAsigned(plantilla);
            
            // si no existe y esta asignado se hara el cambio
            if (detalleOrdenPlantilla == null) {
                detalleOrdenPlantilla = new DetalleOrdenMantenimientoPlantilla();

                detalleOrdenPlantilla.setDeopPlantilla(plantilla);
                detalleOrdenPlantilla.setDeopCostoInsumos(plantilla.getPlmaCostoInsumos());
                detalleOrdenPlantilla.setDeopCostoManoObra(plantilla.getPlmaCostoManoObra());
                detalleOrdenPlantilla.setDeopEstado(0);
                detalleOrdenPlantilla.setDeopCodOrden(orden);
                // generamos el detalle de insumos
                List<DetalleOrdenMantenimientoInsumo> listaDetalleInsumos = generarDetalleInsumos(plantilla, detalleOrdenPlantilla);

                // seteamos la lista de insumos y agregamos el detalleOrdenPlantilla a la lista
                detalleOrdenPlantilla.setDetalleOrdenMantenimientoInsumoCollection(listaDetalleInsumos);
            }

            // se agrega la orden
            listaDetallePlantillas.add(detalleOrdenPlantilla);
        }
        return listaDetallePlantillas;
    }

    private List<DetalleOrdenMantenimientoInsumo> generarDetalleInsumos(PlantillaMantenimiento plantilla, DetalleOrdenMantenimientoPlantilla detalleOrdenPlantilla) {
        DetalleOrdenMantenimientoInsumo detalleOrdenInsumo;
        List<DetalleOrdenMantenimientoInsumo> listaDetalleInsumos;
        // iteramos sobre los detalles de la plantilla para generar
        // la lista de detalles insumos, del detalle plantilla
        listaDetalleInsumos = new ArrayList<DetalleOrdenMantenimientoInsumo>();
        for (DetallePlantillaMantenimiento detallePlantillaMantenimiento : plantilla.getDetallePlantillaMantenimientoCollection()) {
            detalleOrdenInsumo = new DetalleOrdenMantenimientoInsumo();
            detalleOrdenInsumo.setDeoiCantidad(detallePlantillaMantenimiento.getDepmCantidad());
            detalleOrdenInsumo.setDeoiCodInsumo(detallePlantillaMantenimiento.getDepmCodInsumo());
            detalleOrdenInsumo.setDeoiDescripcionUtilizacion("Replicado desde el modelo de plantilla " + plantilla.getPlmaNombre());
            detalleOrdenInsumo.setDeoiDetalleOrden(detalleOrdenPlantilla);
            detalleOrdenInsumo.setDeoiPrecio(detallePlantillaMantenimiento.getDepmCodInsumo().getInsuPrecio());
            listaDetalleInsumos.add(detalleOrdenInsumo);
        }
        return listaDetalleInsumos;
    }

    /**
     * Verifica si la plantilla que se procesara existe en algun detalle.
     *
     * @param plantilla
     * @return
     */
    private DetalleOrdenMantenimientoPlantilla isAsigned(PlantillaMantenimiento plantilla) {
        DetalleOrdenMantenimientoPlantilla retorno = null;
        try {
            for (DetalleOrdenMantenimientoPlantilla detalleOrdenMantenimientoPlantilla : orden.getDetalleOrdenMantenimientoPlantillaCollection()) {
                if (detalleOrdenMantenimientoPlantilla.getDeopPlantilla().equals(plantilla)) {
                    retorno = detalleOrdenMantenimientoPlantilla;
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            return retorno;
        }
    }
}
